Skip to content

fix: Bluetooth chooser reply listener leak + serial path for OS-paired Classic printers#268

Merged
kilbot merged 3 commits into
mainfrom
fix/bluetooth-chooser-session
Jun 12, 2026
Merged

fix: Bluetooth chooser reply listener leak + serial path for OS-paired Classic printers#268
kilbot merged 3 commits into
mainfrom
fix/bluetooth-chooser-session

Conversation

@kilbot

@kilbot kilbot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Users on the 1.9.3 Windows desktop app report the Bluetooth "Scan for printers" button "does nothing". Live diagnosis (macOS dev, instrumented main process) found two issues in the Web Bluetooth chooser flow, plus a structural gap: Web Bluetooth is BLE-only, so Bluetooth Classic printers (most receipt printers) can never appear in the scan.

What

1. select-bluetooth-device listener leak fixed (src/main/bluetooth-select.ts)

Chromium fires select-bluetooth-device repeatedly during one requestDevice() chooser session. The old code registered a new ipcMain.once('bluetooth-device-selected') listener per firing; stale listeners queued FIFO, so a later real selection was consumed by a dead callback and silently dropped. Now one persistent reply listener per window holds the latest chooser callback (invoked exactly once, scoped by event.sender, removed on window close).

2. Serial printing path for OS-paired Bluetooth Classic printers (src/main/serial-printer.ts)

Classic printers paired at the OS level surface as serial devices (/dev/cu.* on macOS, /dev/rfcomm* on Linux). New IPC channels:

  • serial-discovery — enumerates ports via serialport, filters macOS noise (Bluetooth-Incoming-Port etc.), returns {id: 'serial:<path>', name}[]; empty on win32 (the spooler path covers paired printers there).
  • print-raw-serial — validates byte payloads, writes via open→write→drain→close with a 20s timeout and a ghost-print guard (a late open() after timeout closes without writing — prevents duplicate receipts).

Packaging: serialport added to webpack externals + packageAfterPrune runtime copies, including its transitive debug/ms depstest/package-runtime-externals.test.ts now loads serialport from the packaged tree (same as usb), which catches missing modules at test time instead of at packaged-app boot.

Tests

  • New test:bluetooth-select — single persistent listener invariant, latest-callback-wins, stale replies dropped, sender scoping, close cleanup.
  • New test:serial-printer — port filtering/naming, win32 short-circuit, payload validation, write-order, close-on-failure, ghost-print guard.
  • pnpm run test (all 8 suites) + ts:check green on this branch (rebased onto current main).

Companion PR

The renderer side (chooser session state machine, scan UX, paired-printers UI, transport routing) lands in wcpos/monorepo — that PR must not merge before this one + the submodule pointer bump.

kilbot added 3 commits June 13, 2026 00:34
select-bluetooth-device fires repeatedly per chooser session; registering an
ipcMain.once listener per firing queued stale callbacks that consumed real
selections FIFO. Keep one persistent listener holding the latest callback.
serial-discovery lists /dev/cu.* (macOS) / serial ports (Linux) via serialport;
print-raw-serial writes raw ESC/POS bytes with open/write/drain/close + timeout.
Windows excluded — the spooler path covers paired printers there.
debug/ms escape the @serialport namespace copy and must ship as runtime
externals — the packaged-require test now loads serialport from the packaged
tree like usb. A timed-out print no longer writes when open() resolves late;
empty serial device keys are rejected up front.
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@kilbot, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour, 43 minutes, and 24 seconds. Learn how PR review limits work.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: wcpos/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d5c50f24-1eb4-4ea1-91c2-f575dd7e6d09

📥 Commits

Reviewing files that changed from the base of the PR and between 2ea958c and 8ab4894.

📒 Files selected for processing (10)
  • forge.config.ts
  • package.json
  • src/index.ts
  • src/main/bluetooth-select.test.ts
  • src/main/bluetooth-select.ts
  • src/main/serial-printer.test.ts
  • src/main/serial-printer.ts
  • src/preload.ts
  • test/package-runtime-externals.test.ts
  • webpack.main.config.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bluetooth-chooser-session

Comment @coderabbitai help to get the list of available commands and usage tips.

@kilbot kilbot merged commit 5efe05f into main Jun 12, 2026
7 checks passed
@kilbot kilbot deleted the fix/bluetooth-chooser-session branch June 12, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant